using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using needle.UI.Controls.Flat.Windows.Docking;
using needle.UI.Controls.Flat.Windows.Forms.Collections;

namespace needle.UI.Controls.Flat.Windows.Docking
{
    public class RedockerContent : Redocker
    {
		#region Nested types

		public enum Source
        {
            RawContent,
            ContentInsideWindow,
            WindowContent,
            FloatingForm
        }

		#endregion

        protected static int mhotVectorFromEdge = 2;
        protected static int mhotVectorBeforeControl = 5;

        protected ContainerControl mcontainer;
        protected Source msource;
        protected Content mcontent;
        protected HotZone mcurrentHotZone;
        protected Control mcallingControl;
        protected FloatingForm mfloatingForm;
        protected HotZoneCollection mhotZones;
        protected WindowContent mwindowContent;
        protected DockingManager mdockingManager;
        protected Rectangle minsideRect;
        protected Rectangle moutsideRect;
        protected Point moffset;

		#region Constructors

		public RedockerContent(Control callingControl, Content c, Point offset)
        {
            InternalConstruct(callingControl, Source.RawContent, c, null, null, c.DockingManager, offset);
        }

        public RedockerContent(Control callingControl, Content c, WindowContent wc, Point offset)
        {
            InternalConstruct(callingControl, Source.ContentInsideWindow, c, wc, null, c.DockingManager, offset);
        }

        public RedockerContent(Control callingControl, WindowContent wc, Point offset)
        {
            InternalConstruct(callingControl, Source.WindowContent, null, wc, null, wc.DockingManager, offset);
        }

        public RedockerContent(FloatingForm ff, Point offset)
        {
            InternalConstruct(ff, Source.FloatingForm, null, null, ff, ff.DockingManager, offset);
        }

        protected void InternalConstruct(Control callingControl, 
                                         Source source, 
                                         Content c, 
                                         WindowContent wc, 
                                         FloatingForm ff,
                                         DockingManager dm,
                                         Point offset)
        {
            mcallingControl = callingControl;
            msource = source;
            mcontent = c;
            mwindowContent = wc;
            mdockingManager = dm;
            mcontainer = mdockingManager.Container;
            mfloatingForm = ff;
            mhotZones = null;
            mcurrentHotZone = null;
            minsideRect = new Rectangle();
            moutsideRect = new Rectangle();
            moffset = offset;

            EnterTrackingMode();
        }

		#endregion

		#region Overrides

		public override void EnterTrackingMode()
        {
            if (!mtracking)
            {
                base.EnterTrackingMode();

                if (mdockingManager == null)
                    throw new ArgumentNullException("DockingManager");

                GenerateHotZones();
            }
        }

        public override bool ExitTrackingMode(MouseEventArgs e)
        {
            if (mtracking)
            {
                base.ExitTrackingMode(e);
	
                if (mcurrentHotZone != null)
                {
					Point mousePos = mcallingControl.PointToScreen(new Point(e.X, e.Y));
					
					return mcurrentHotZone.ApplyChange(mousePos, this);
                }
            }

            return false;
        }

        public override void QuitTrackingMode(MouseEventArgs e)
        {
            if (mtracking)
            {
                if (mcallingControl.Handle != IntPtr.Zero)
                {
                    if (mcurrentHotZone != null)
                        mcurrentHotZone.RemoveIndicator(new Point(0,0));
                }
                                    
                base.QuitTrackingMode(e);
            }
        }

        public override void OnMouseMove(MouseEventArgs e)
        {
			if (mcallingControl.Handle != IntPtr.Zero)
			{
				Point mousePos = mcallingControl.PointToScreen(new Point(e.X, e.Y));

				HotZone hz = mhotZones.Contains(mousePos);

				if (hz != mcurrentHotZone)
				{
					if (mcurrentHotZone != null)
						mcurrentHotZone.RemoveIndicator(mousePos);

					mcurrentHotZone = hz;

					if (mcurrentHotZone != null)
						mcurrentHotZone.DrawIndicator(mousePos);
				}
				else
				{
					if (mcurrentHotZone != null)
						mcurrentHotZone.UpdateForMousePosition(mousePos, this);
				}
			}

            base.OnMouseMove(e);
        }

        public override bool OnMouseUp(MouseEventArgs e)
        {
			if (mcallingControl.Handle != IntPtr.Zero)
			{
				if (mcurrentHotZone != null)
					mcurrentHotZone.RemoveIndicator(mcallingControl.PointToScreen(new Point(e.X, e.Y)));
			}

            return base.OnMouseUp(e);
        }

		#endregion

		#region Virtuals

        protected void GenerateHotZones()
        {
            Rectangle formClient = mcontainer.RectangleToScreen(mcontainer.ClientRectangle);

            mhotZones = new HotZoneCollection();

            int outerIndex = FindOuterIndex();

            ArrayList topList = new ArrayList();
            ArrayList leftList = new ArrayList();
            ArrayList rightList = new ArrayList();
            ArrayList bottomList = new ArrayList();
            ArrayList fillList = new ArrayList();

            PreProcessControlsCollection(topList, leftList, rightList, bottomList, fillList, outerIndex);

            int vectorH;
            int vectorV;

            VectorDependsOnSourceAndState(out vectorH, out vectorV);

            GenerateHotZonesForTop(topList, formClient, vectorV, outerIndex);
            GenerateHotZonesForLeft(leftList, formClient, vectorH, outerIndex);
            GenerateHotZonesForRight(rightList, formClient, vectorH, outerIndex);
            GenerateHotZonesForBottom(bottomList, formClient, vectorV, outerIndex);
            GenerateHotZonesForFill(fillList, outerIndex);
            GenerateHotZonesForFloating(SizeDependsOnSource());
        }

        protected void PreProcessControlsCollection(ArrayList topList, ArrayList leftList, ArrayList rightList, ArrayList bottomList, ArrayList fillList, int outerIndex)
        {
            minsideRect = mcontainer.ClientRectangle; 
            moutsideRect = minsideRect; 

            foreach(Control item in mcontainer.Controls)
            {
                bool ignoreType = (item is AutoHidePanel);

                int controlIndex = mcontainer.Controls.IndexOf(item);

                bool outer = (controlIndex >= outerIndex);

                if (item.Visible)
                {
                    if (item.Dock == DockStyle.Top)
                    {
                        topList.Insert(0, item);

                        if (minsideRect.Y < item.Bottom)
                        {
                            minsideRect.Height -= item.Bottom - minsideRect.Y;
                            minsideRect.Y = item.Bottom;
                        }

                        if (outer || ignoreType)
                        {
                            if (moutsideRect.Y < item.Bottom)
                            {
                                moutsideRect.Height -= item.Bottom - moutsideRect.Y;
                                moutsideRect.Y = item.Bottom;
                            }
                        }
                    }

                    if (item.Dock == DockStyle.Left)
                    {
                        leftList.Insert(0, item);

                        if (minsideRect.X < item.Right)
                        {
                            minsideRect.Width -= item.Right - minsideRect.X;
                            minsideRect.X = item.Right;
                        }

                        if (outer || ignoreType)
                        {
                            if (moutsideRect.X < item.Right)
                            {
                                moutsideRect.Width -= item.Right - moutsideRect.X;
                                moutsideRect.X = item.Right;
                            }
                        }
                    }

                    if (item.Dock == DockStyle.Bottom)
                    {
                        bottomList.Insert(0, item);

                        if (minsideRect.Bottom > item.Top)
                            minsideRect.Height -= minsideRect.Bottom - item.Top;

                        if (outer || ignoreType)
                        {
                            if (moutsideRect.Bottom > item.Top)
                                moutsideRect.Height -= moutsideRect.Bottom - item.Top;
                        }
                    }

                    if (item.Dock == DockStyle.Right)
                    {
                        rightList.Insert(0, item);

                        if (minsideRect.Right > item.Left)
                            minsideRect.Width -= minsideRect.Right - item.Left;

                        if (outer || ignoreType)
                        {
                            if (moutsideRect.Right > item.Left)
                                moutsideRect.Width -= moutsideRect.Right - item.Left;
                        }
                    }

                    if (item.Dock == DockStyle.Fill)
						fillList.Insert(0, item);
                }
            }

            minsideRect = mcontainer.RectangleToScreen(minsideRect);
            moutsideRect = mcontainer.RectangleToScreen(moutsideRect);
        }

        protected int FindOuterIndex()
        {
            int outerIndex = mcontainer.Controls.Count;
			
            Control outerControl = mdockingManager.OuterControl;

            if (outerControl != null)
                outerIndex = mcontainer.Controls.IndexOf(outerControl);

            return outerIndex;
        }

        protected void GenerateHotZonesForLeft(ArrayList leftList, Rectangle formClient, int vector, int outerIndex)
        {
            foreach(Control c in leftList)
            {	
                bool ignoreType = (c is AutoHidePanel);
            
                int controlIndex = mcontainer.Controls.IndexOf(c);

                if (!ignoreType && (controlIndex < outerIndex))
                {
                    Rectangle hotArea = c.RectangleToScreen(c.ClientRectangle);

                    hotArea.Width = mhotVectorBeforeControl;

                    Rectangle newSize = new Rectangle(hotArea.X, hotArea.Y, vector, hotArea.Height);
					
					hotArea.X += mhotVectorFromEdge;

                    mhotZones.Add(new HotZoneReposition(hotArea, newSize, State.DockLeft, controlIndex));

                    IHotZoneSource ag = c as IHotZoneSource;

                    if (ag != null)
                        ag.AddHotZones(this, mhotZones);
                }
            }

            Rectangle fullArea = moutsideRect;

            fullArea.Width = mhotVectorFromEdge;

            Rectangle fillSize = new Rectangle(fullArea.X, fullArea.Y, vector, fullArea.Height);

            mhotZones.Add(new HotZoneReposition(fullArea, fillSize, State.DockLeft, false));

			if (!mdockingManager.InsideFill)
			{
				Rectangle innerHotArea = new Rectangle(minsideRect.X, minsideRect.Y, mhotVectorBeforeControl, minsideRect.Height);

				Rectangle innerNewSize = new Rectangle(innerHotArea.X, innerHotArea.Y, vector, innerHotArea.Height);

				mhotZones.Add(new HotZoneReposition(innerHotArea, innerNewSize, State.DockLeft, true));
			}
        }

        protected void GenerateHotZonesForRight(ArrayList rightList, Rectangle formClient, int vector, int outerIndex)
        {
            foreach(Control c in rightList)
            {	
                bool ignoreType = (c is AutoHidePanel);
            
                int controlIndex = mcontainer.Controls.IndexOf(c);

                if (!ignoreType && (controlIndex < outerIndex))
                {
                    Rectangle hotArea = c.RectangleToScreen(c.ClientRectangle);

                    hotArea.X = hotArea.Right - mhotVectorBeforeControl;
                    hotArea.Width = mhotVectorBeforeControl;

                    Rectangle newSize = new Rectangle(hotArea.Right - vector, hotArea.Y, vector, hotArea.Height);
					
					hotArea.X -= mhotVectorFromEdge;

                    mhotZones.Add(new HotZoneReposition(hotArea, newSize, State.DockRight, controlIndex));

                    IHotZoneSource ag = c as IHotZoneSource;

                    if (ag != null)
                        ag.AddHotZones(this, mhotZones);
                }
            }

            Rectangle fullArea = moutsideRect;

            fullArea.X = fullArea.Right - mhotVectorFromEdge;
            fullArea.Width = mhotVectorFromEdge;

            Rectangle fillSize = new Rectangle(fullArea.Right - vector, fullArea.Y, vector, fullArea.Height);

            mhotZones.Add(new HotZoneReposition(fullArea, fillSize, State.DockRight, false));

			if (!mdockingManager.InsideFill)
			{
				Rectangle innerHotArea = new Rectangle(minsideRect.Right - mhotVectorBeforeControl, minsideRect.Y, mhotVectorBeforeControl, minsideRect.Height);

				Rectangle innerNewSize = new Rectangle(innerHotArea.Right - vector, innerHotArea.Y, vector, innerHotArea.Height);

				mhotZones.Add(new HotZoneReposition(innerHotArea, innerNewSize, State.DockRight, true));
			}
        }

        protected void GenerateHotZonesForTop(ArrayList topList, Rectangle formClient, int vector, int outerIndex)
        {
            foreach(Control c in topList)
            {	
                bool ignoreType = (c is AutoHidePanel);
            
                int controlIndex = mcontainer.Controls.IndexOf(c);

                if (!ignoreType && (controlIndex < outerIndex))
                {
                    Rectangle hotArea = c.RectangleToScreen(c.ClientRectangle);

                    hotArea.Height = mhotVectorBeforeControl;

                    Rectangle newSize = new Rectangle(hotArea.X, hotArea.Y, hotArea.Width, vector);
					
					hotArea.Y += mhotVectorFromEdge;

                    mhotZones.Add(new HotZoneReposition(hotArea, newSize, State.DockTop, controlIndex));

                    IHotZoneSource ag = c as IHotZoneSource;

                    if (ag != null)
                        ag.AddHotZones(this, mhotZones);
                }
            }

            Rectangle fullArea = moutsideRect;

            fullArea.Height = mhotVectorFromEdge;

            Rectangle fillSize = new Rectangle(fullArea.X, fullArea.Y, fullArea.Width, vector);

            mhotZones.Add(new HotZoneReposition(fullArea, fillSize, State.DockTop, false));

			if (!mdockingManager.InsideFill)
			{
				Rectangle innerHotArea = new Rectangle(minsideRect.X, minsideRect.Y, minsideRect.Width, mhotVectorBeforeControl);

				Rectangle innerNewSize = new Rectangle(innerHotArea.X, innerHotArea.Y, innerHotArea.Width, vector);

				mhotZones.Add(new HotZoneReposition(innerHotArea, innerNewSize, State.DockTop, true));
			}
        }

        protected void GenerateHotZonesForBottom(ArrayList bottomList, Rectangle formClient, int vector, int outerIndex)
        {
            foreach(Control c in bottomList)
            {	
                bool ignoreType = (c is AutoHidePanel);
            
                int controlIndex = mcontainer.Controls.IndexOf(c);

                if (!ignoreType && (controlIndex < outerIndex))
                {
                    Rectangle hotArea = c.RectangleToScreen(c.ClientRectangle);

                    hotArea.Y = hotArea.Bottom - mhotVectorBeforeControl;
                    hotArea.Height = mhotVectorBeforeControl;

                    Rectangle newSize = new Rectangle(hotArea.X, hotArea.Bottom - vector, hotArea.Width, vector);
					
					hotArea.Y -= mhotVectorFromEdge;

                    mhotZones.Add(new HotZoneReposition(hotArea, newSize, State.DockBottom, controlIndex));

                    IHotZoneSource ag = c as IHotZoneSource;

                    if (ag != null)
                        ag.AddHotZones(this, mhotZones);
                }
            }

            Rectangle fullArea = moutsideRect;

            fullArea.Y = fullArea.Bottom - mhotVectorFromEdge;
            fullArea.Height = mhotVectorFromEdge;

            Rectangle fillSize = new Rectangle(fullArea.X, fullArea.Bottom - vector, fullArea.Width, vector);

            mhotZones.Add(new HotZoneReposition(fullArea, fillSize, State.DockBottom, false));

			if (!mdockingManager.InsideFill)
			{
				Rectangle innerHotArea = new Rectangle(minsideRect.X, minsideRect.Bottom - mhotVectorBeforeControl, minsideRect.Width, mhotVectorBeforeControl);

				Rectangle innerNewSize = new Rectangle(innerHotArea.X, innerHotArea.Bottom - vector, innerHotArea.Width, vector);

				mhotZones.Add(new HotZoneReposition(innerHotArea, innerNewSize, State.DockBottom, true));
			}
        }

        protected void GenerateHotZonesForFill(ArrayList fillList, int outerIndex)
        {
            foreach(Control c in fillList)
            {	
                bool ignoreType = (c is AutoHidePanel);
            
                int controlIndex = mcontainer.Controls.IndexOf(c);

                if (controlIndex < outerIndex)
                {
                    IHotZoneSource ag = c as IHotZoneSource;

                    if (ag != null)
                        ag.AddHotZones(this, mhotZones);
                }
            }
        }

        protected void GenerateHotZonesForFloating(Size sourceSize)
        {
            ContainerControl main = mdockingManager.Container;
            
			if (main != null)
			{
				Form mainForm = main.FindForm();

				if (mainForm != null)
				{
					foreach(Form f in mainForm.OwnedForms)
					{
						if (f != mfloatingForm)
						{
							IHotZoneSource ag = f as IHotZoneSource;

							if (ag != null)
								ag.AddHotZones(this, mhotZones);
						}
					}
				}
			}
             
            Rectangle hotArea = SystemInformation.VirtualScreen;

            Rectangle newSize = new Rectangle(0, 0, sourceSize.Width, sourceSize.Height);

            mhotZones.Add(new HotZoneFloating(hotArea, newSize, moffset, this)); 
        }

        protected void VectorDependsOnSourceAndState(out int vectorH, out int vectorV)
        {
            Size sourceSize = SizeDependsOnSource();

            switch(msource)
            {
                case Source.FloatingForm:
					if (sourceSize.Width > sourceSize.Height)
						sourceSize.Width = sourceSize.Height;

					if (sourceSize.Height > sourceSize.Width)
						sourceSize.Height = sourceSize.Width;

					if (sourceSize.Width > (mcontainer.Width / 2))
						sourceSize.Width = mcontainer.Width / 2;

					if (sourceSize.Height > (mcontainer.Height / 2))
						sourceSize.Height = mcontainer.Height / 2;
					break;
                case Source.WindowContent:
                case Source.ContentInsideWindow:
                switch(mwindowContent.State)
                {
                    case State.DockLeft:
                    case State.DockRight:
                        vectorH = sourceSize.Width;
                        vectorV = vectorH;
                        return;
                    case State.DockTop:
                    case State.DockBottom:
                        vectorH = sourceSize.Height;
                        vectorV = vectorH;
                        return;
                }
                break;
            }

            vectorV = sourceSize.Height;
            vectorH = sourceSize.Width;
        }

        protected Size SizeDependsOnSource()
        {
            switch(msource)
            {
                case Source.WindowContent:
                    return mwindowContent.Size;
                case Source.FloatingForm:
                    return mfloatingForm.Size;
                case Source.RawContent:
                case Source.ContentInsideWindow:
                default:
                    return mcontent.DisplaySize;
            }
        }

		#endregion

		#region Properties

		public Source DockingSource
		{
			get { return msource; }
		}

		public Content Content
		{
			get { return mcontent; }
		}

		public WindowContent WindowContent
		{
			get { return mwindowContent; }
		}

		public DockingManager DockingManager
		{
			get { return mdockingManager; }
		}

		public FloatingForm FloatingForm
		{
			get { return mfloatingForm; }
		}

		#endregion
    }
}
